package com.seven.commons;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Objects;

import static com.seven.commons.StringPool.COMMA;

/**
 * @author sevenDay
 * @classname GlobalException
 * @description: 描述TODO
 * @date 2017/12/13
 */
@RestControllerAdvice
public class GlobalException {
    private final static Logger logger = LoggerFactory.getLogger(GlobalException.class);


    /**
     * 生产环境
     */
    private final static String ENV_PROD = "prod";

    private final static String UNKNOWN = "unknown";


    @Value("${spring.profiles.active}")
    private String profile;
    /**
     * @param e
     * @return BaseDataJson<Object>
     * @方法名: handle
     * @描述: TODO HttpMessageNotReadableException
     * org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.seven.commons.BaseResponse com.dtk.points.controller.TuserConfigController.addConfigByUid(com.dtk.points.pojo.request.TuserConfigParams)
     * at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:160)
     * at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130)
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public BaseResponse handle(Exception e, HttpServletRequest request, HttpServletResponse response) {
        /*进行系统内部的处理异常和反馈情况start*/
        if (e instanceof HttpRequestMethodNotSupportedException) {
            HttpRequestMethodNotSupportedException exception = (HttpRequestMethodNotSupportedException)e;
            StringBuilder builder = new StringBuilder();
            builder.append("请求IP:"+request.getRemoteHost()+"\n"+"请求路径:"+request.getRequestURI());
            builder.append("\n请求的方法是:"+exception.getMethod()+"\n支持的方法是:"+JsonUtils.objectToJson(exception.getSupportedMethods()));
            logger.error("HttpRequestMethodNotSupportedException："+"\n"+builder.toString());
            return new BaseResponse(BaseResultEnum.HTTP_REQUEST_NOT_FOUND);

        }else if(e instanceof HttpMessageNotReadableException){
            HttpMessageNotReadableException exception = (HttpMessageNotReadableException)e;
            logger.error("运行时异常:"+JsonUtils.objectToJson(getRequestParams(request)));
            logger.error("系统异常", e);
            return new BaseResponse(BaseResultEnum.HTTP_REQUEST_PARAM_TYPE_ERROR);
        }
        else if (e instanceof NoHandlerFoundException){
            NoHandlerFoundException exception = (NoHandlerFoundException)e;
            StringBuilder builder = new StringBuilder();
            builder.append("请求IP:"+request.getRemoteHost()+"\n"+"请求路径:"+request.getRequestURI());
            builder.append("\n请求的方法是:"+exception.getHttpMethod()+"\n请求的路径:"+exception.getRequestURL());
            logger.error("NoHandlerFoundException："+"\n"+builder.toString());
            return new BaseResponse(BaseResultEnum.HTTP_REQUEST_NOT_FOUND);
        }else if(e instanceof MethodArgumentTypeMismatchException){
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException)e;
            StringBuilder builder = new StringBuilder();
            builder.append("请求IP:"+request.getRemoteHost()+"\n"+"请求路径:"+request.getRequestURI());
            builder.append("\n"+"请求的方法:"+exception.getParameter().getMethod().getName()+"\n"+"请求参数:"+exception.getName()+"\n"+"请求信息:"+exception.getLocalizedMessage());
            logger.error("参数匹配："+"\n"+builder.toString());
            return new BaseResponse(BaseResultEnum.HTTP_REQUEST_ARGUMENT_TYPE_ERROR);
        }
        //进行系统内部的处理异常和反馈情况end logger.error("",e)，因为自定义的业务异常，堆栈信息在并发量很大情况下，会增大log的日志信息
       else if (e instanceof AbstractSevenException) {
            AbstractSevenException baseException = (AbstractSevenException) e;
            return new BaseResponse(baseException.getCode(), baseException.getMessage());
        }
       else {
            //注意记录打印请求的参数，请求头，和相关的信息，请求方法
            //#根据不同的环境打印不同的错误信息，方便调试处理
            if (ENV_PROD.equals(profile)) {
                logger.error("运行时异常:"+JsonUtils.objectToJson(getRequestParams(request)));
                logger.error("系统异常", e);
                return BaseResponse.buildFail();
            }else{
                logger.error("运行时异常:"+JsonUtils.objectToJson(getRequestParams(request)));
                logger.error("系统异常", e);
                return BaseResponse.buildFail(BaseResultEnum.UNKNOWN_ERROR.getCode(),e.getMessage());
            }

        }
    }
    private HttpRequestParams getRequestParams(HttpServletRequest request) {
        HttpRequestParams httpRequestParams = new HttpRequestParams();
        httpRequestParams.setRequestUrI(request.getRequestURI());
        httpRequestParams.setRequestMethods(request.getMethod());
        Map<String, String[]> parameterMap = request.getParameterMap();
        httpRequestParams.setRequestParams(getParams(parameterMap));
        Enumeration<String> headerNames = request.getHeaderNames();
        httpRequestParams.setRequestHeadParams(getHeadParams(headerNames,request));
        httpRequestParams.setRequestBodyParams(getBodyData(request));
        httpRequestParams.setRemoteAddr(getIpAddress(request));
        return httpRequestParams;
    }

    /**
     * 拼装params的信息
     * @param paramsMap
     * @return
     */
    private String getParams(Map<String, String[]> paramsMap) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String[]> entry : paramsMap.entrySet()) {
            String[] value=entry.getValue();
            String values = null;
            if (!Objects.isNull(value)&&value.length>0){

                StringBuffer b = new StringBuffer();
                for (int i = 0; i < value.length; i++) {
                    b.append(" "+value[i]);
                }
                values = b.toString();
            }
            builder.append("key = " + entry.getKey() + ", value = " + values+"  ");
        }
        return builder.toString();

    }

    /**
     * 获取请求头的信息
     * @param headers
     * @param request
     * @return
     */
    private String getHeadParams(Enumeration<String> headers, HttpServletRequest request){

        StringBuffer buffer = new StringBuffer();
        while (headers.hasMoreElements()) {
            String key = headers.nextElement();
            String value = request.getHeader(key);
            buffer.append("key = " + key + ", value = " + value+"  ");
        }
        return buffer.toString();
    }

    /**
     * 读取body的内容
     * HttpServletRequest 请求中的 body 内容仅能调用 request.getInputStream()， request.getReader()方法读取一次，
     * 重复读取会报 java.io.IOException: Stream closed 异常
     *
     * @param request
     * @return
     */
    private String getBodyData(HttpServletRequest request) {
        BufferedReader reader = null;
        StringBuffer data = new StringBuffer();
        try {
            String line = StringPool.EMPTY;
            reader = request.getReader();
            while (null != (line = reader.readLine())) {
                data.append(line);
            }
        } catch (IOException e) {
            logger.error("get request params error: ", e);
        } finally {
            if (null == reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("close BufferedReader error: ", e);
                }
            }
        }
        return data.toString();
    }

    /**
     * 获取真实ip地址
     * @param request
     * @return
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (logger.isInfoEnabled()) {
            logger.info("getIpAddress(HttpServletRequest) - X-Forwarded-For - String ip=[{}]" , ip);
        }
        if (ip == null || ip.length() == 0 ||UNKNOWN.equalsIgnoreCase(ip)) {
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
                if (logger.isInfoEnabled()) {
                    logger.info("getIpAddress(HttpServletRequest) - Proxy-Client-IP - String ip=[{}]" , ip);
                }
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
                if (logger.isInfoEnabled()) {
                    logger.info("getIpAddress(HttpServletRequest) - WL-Proxy-Client-IP - String ip=[{}]" , ip);
                }
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
                if (logger.isInfoEnabled()) {
                    logger.info("getIpAddress(HttpServletRequest) - HTTP_CLIENT_IP - String ip=[{}]" , ip);
                }
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
                if (logger.isInfoEnabled()) {
                    logger.info("getIpAddress(HttpServletRequest) - HTTP_X_FORWARDED_FOR - String ip=[{}]" , ip);
                }
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
                if (logger.isInfoEnabled()) {
                    logger.info("getIpAddress(HttpServletRequest) - getRemoteAddr - String ip=[{}]" , ip);
                }
            }
        } else if (ip.length() > 15) {
            String[] ips = ip.split(COMMA);
            for (int index = 0; index < ips.length; index++) {
                String strIp = ips[index];
                if (!(UNKNOWN.equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }


}
